# Ubuntu下Eclipse 开发嵌入式Linux
## Ubuntu安装Eclipse
1. 下载Eclipse:Eclipse官网下载链接: https://www.eclipse.org/downloads/packages/

**解压安装包到 /opt目录下**
```bash
sudo tar -zxvf eclipse-cpp-2020-09-R-linux-gtk-x86_64.tar.gz -C /opt/
```
**创建桌面快捷方式**
```bash
cd /usr/share/applications
sudo vim eclipse.desktop
# 文件内容如下:
[Desktop Entry]
Encoding=UTF-8
Name=Eclipse
Comment=Eclipse
Exec=/opt/eclipse/eclipse
Icon=/opt/eclipse/icon.xpm
Terminal=false
StartupNotify=true
Type=Application
Categories=Application;Development;
# 给文件加权限
sudo chmod u+x eclipse.desktop
# 在/usr/share/applications目录下将Eclipse图标复制到桌面即可
```
2. 下载JDK:Eclipse必要的运行环境
下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html

**配置JDK**
```bash
sudo mkdir /opt/jvm
sudo tar zxvf jdk-8u271-linux-x64.tar.gz -C /opt/jvm
```
**配置环境变量**
```bash
sudo vim /etc/profile
# 添加下面的代码
export JAVA_HOME=/opt/jvm/jdk1.8.0_271
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:%{JAVA_HOME}/lib:%{JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH
```
验证jdk是否安装成功
```bash
java -version
```
## Ubuntu使用Eclipse 开发嵌入式Linux驱动程序
1. 打开Eclipse新建一个C Project,在以下界面需要注意Toolchains栏目选择交叉编译环境Cross GCC 。注意:Linux Gcc是ubuntu自带的编译器。

2. 下一步到到Cross GCC Command窗口,做以下配置,选择自己的交叉编译器的安装路径

3. 建立好工程后,按"ALT+ENTER"快捷键,调出Properties窗口,选中"C/C++ Build"
修改:不勾选Generate Makefiles automatically(不自动生成Makefile,使用自己编写的)

4. 添加linux内核头文件,下面以IMX6ull说明。

点击【C/C++ General 】->【 Paths and Symbols】 -> 【Export Setting...】将当前配置导出为symbols.xml文件进行保存(该文件修改好后,还需要从Import Setting导入进来)
5. 在 【ebf_6ull_linux/include/generated/ 】目录下执行命令, 完成后在该目录下生成一个symbols_linux.xml文件
```bash
cd ebf_6ull_linux/include/generated/
cat autoconf.h |grep define |awk '{print "" $2 "" $3 ""}' > symbols_linux.xml
```
执行过程如下

在symbols_linux.xml文件的开头增加语句:
```bash
__KERNEL__1
```
修改后的截图如下:

6. 打开之前从eclipse导出的symbols.xml文件如下两处进行修改:

上图中,第一部分修改添加内核头文件,根据自己的情况添加,我的内容如下:
```
/home/book/embedfire/ebf_6ull_linux/include
/home/book/embedfire/ebf_6ull_linux/arch/arm/include
/home/book/embedfire/ebf_6ull_linux/arch/arm/mach-imx
/home/book/embedfire/ebf_6ull_linux/arch/arm/mach-imx/devices
```
7. 将修改好的symbols.xml文件重新导入Eclipse

8. Eclipse项目下新建led驱动程序,代码如下
- `board_fire_imx6ull_pro.c`
```c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "board_fire_imx6ull_pro.h"
static volatile unsigned int *CCM_CCGR1 ;
static volatile unsigned int *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;
static volatile unsigned int *GPIO5_GDIR ;
static volatile unsigned int *GPIO5_DR ;
void hw_led_init(void)
{
int val;
if (!CCM_CCGR1){
CCM_CCGR1 = ioremap(0x20C406C, 4);
IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = ioremap(0x2290014, 4);
GPIO5_GDIR = ioremap(0x020AC000 + 0x4, 4);
GPIO5_DR = ioremap(0x020AC000 + 0, 4);
}
/* GPIO5_IO03 */
/* a. 使能GPIO5
* set CCM to enable GPIO5
* CCM_CCGR1[CG15] 0x20C406C
* bit[31:30] = 0b11
*/
*CCM_CCGR1 |= (3<<30);
/* b. 设置GPIO5_IO03用于GPIO
* set IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3
* to configure GPIO5_IO03 as GPIO
* IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 0x2290014
* bit[3:0] = 0b0101 alt5
*/
val = *IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3;
val &= ~(0xf);
val |= (5);
*IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 = val;
/* b. 设置GPIO5_IO03作为output引脚
* set GPIO5_GDIR to configure GPIO5_IO03 as output
* GPIO5_GDIR 0x020AC000 + 0x4
* bit[3] = 0b1
*/
*GPIO5_GDIR |= (1<<3);
}
void hw_led_reinit(void)
{
if (CCM_CCGR1){
iounmap(CCM_CCGR1);
iounmap(IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3);
iounmap( GPIO5_GDIR);
iounmap(GPIO5_DR);
}
CCM_CCGR1 =NULL;/* 这里切记要清空,要不然下次从新装载驱动的时候由于CCM_CCGR1不为空的话,初始化函数不会从新ioremop */
IOMUXC_SNVS_SW_MUX_CTL_PAD_SNVS_TAMPER3 =NULL;
GPIO5_GDIR=NULL;
GPIO5_DR=NULL;
}
void hw_led_write (int index, int state) /* 控制LED, which-哪个LED, status:1-亮,0-灭 */
{
switch (index){
case 0:
if (state) {
/* on: output 0*/
/* d. 设置GPIO5_DR输出低电平
* set GPIO5_DR to configure GPIO5_IO03 output 0
* GPIO5_DR 0x020AC000 + 0
* bit[3] = 0b0
*/
*GPIO5_DR &= ~(1<<3);
} else {
/* off: output 1*/
/* e. 设置GPIO5_IO3输出高电平
* set GPIO5_DR to configure GPIO5_IO03 output 1
* GPIO5_DR 0x020AC000 + 0
* bit[3] = 0b1
*/
*GPIO5_DR |= (1<<3);
}
break;
default:
break;
}
/*
* 这里要根据次设备号which决定控制哪个灯,用status决定是否亮灭
*/
printk("%s %s line %d, led %d, %s\n", __FILE__, __FUNCTION__, __LINE__, index, state ? "on" : "off");
}
```
- `board_fire_imx6ull_pro.h`
```c
#ifndef _BOARD_FIRE_IMX6ULL_PRO_H
#define _BOARD_FIRE_IMX6ULL_PRO_H
void hw_led_init(void);
void hw_led_write(int index,int state);
void hw_led_reinit(void);
#endif
```
- `led_drv.c`
```c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "board_fire_imx6ull_pro.h"
//#include "board_rk3288.h"
struct led_char_dev {
dev_t devid; /* 字符ID */
struct class *class; /* 类 */
struct device *device; /* 设备 */
int major; /* 主设备号 */
int minor; /* 次设备号 */
};
struct led_char_dev g_led_dev; /* 定义led设备 */
/* 3. 实现对应的open/read/write等函数,填入file_operations结构体 */
static ssize_t led_drv_read (struct file *file, char __user *buf, size_t size, loff_t *offset)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
/* write(fd, &val, 1); */
static ssize_t led_drv_write (struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
unsigned long err;
uint8_t status = 0;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
err = copy_from_user(&status, buf, 1);
/* 根据次设备号和status控制LED */
hw_led_write(0, status);
return 1;
}
static int led_drv_open (struct inode *node, struct file *file)
{
hw_led_init();
file->private_data = &g_led_dev;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
}
static int led_drv_close (struct inode *node, struct file *file)
{
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
hw_led_reinit();
return 0;
}
/* 2. 定义自己的file_operations结构体 */
static struct file_operations led_drv = {
.owner = THIS_MODULE,
.open = led_drv_open,
.read = led_drv_read,
.write = led_drv_write,
.release = led_drv_close,
};
/**
* @description: led_init - 入口函数
*/
static int __init led_init(void)
{
/*
* 1: 自动获取主设备号
*/
g_led_dev.major = register_chrdev(0, "xym_led", &led_drv); /* 注册字符设备驱动 */
g_led_dev.class = class_create(THIS_MODULE, "xym_led_class");
if (IS_ERR(g_led_dev.class)) {
unregister_chrdev(g_led_dev.major, "xym_led");
goto led_init_error;
}
/*
* 2.创建设备
*/
g_led_dev.devid = MKDEV(g_led_dev.major,0);
device_create(g_led_dev.class, NULL, g_led_dev.devid, NULL, "xym_led"); /* /dev/xym_led */
printk("%s %s line %d:insmod !\n", __FILE__, __FUNCTION__, __LINE__);
return 0;
led_init_error:
printk("%s %s line %d:init error !!!\n", __FILE__, __FUNCTION__, __LINE__);
return -1;
}
/**
* @description: led_exit - 出口函数
*/
static void __exit led_exit(void)
{
hw_led_reinit();
device_destroy(g_led_dev.class, g_led_dev.devid); /* /dev/xym_led */
class_destroy(g_led_dev.class);
unregister_chrdev(g_led_dev.major, "xym_led");
printk("%s %s line %d: rmmod ! \n", __FILE__, __FUNCTION__, __LINE__);
}
module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xym_@163.com");
```
- `led_app.c`
该应用程序点亮中间的led灯,用来测试上面的驱动文件是否ok
```c
#include
#include
#include
#include
#include
#include
/*
* ./led_app on
* ./led_app off
*/
#define led "/dev/xym_led"
// echo none >> /sys/class/leds/cpu/trigger
#include
int main(int argc, char **argv)
{
int fd;
char status;
/* 1. 判断参数 */
if (argc != 2) {
printf("Usage: %s \n", argv[0]);
return -1;
}
/* 2. 打开文件 */
fd = open(led, O_RDWR);
if (fd == -1){
return -1;
}
/* 3. 写文件 */
if (0 == strcmp(argv[1], "on")){
status = 1;
write(fd, &status, 1);
}else{
status = 0;
write(fd, &status, 1);
}
close(fd);
return 0;
}
```
9. Eclipse项目下新建**Makefile**文件,内容如下
```bash
# 1. 使用不同的开发板内核时, 一定要修改KERN_DIR
# 2. KERN_DIR中的内核要事先配置、编译, 为了能编译内核, 要先设置下列环境变量:
# 2.1 ARCH, 比如: export ARCH=arm64
# 2.2 CROSS_COMPILE, 比如: export CROSS_COMPILE=aarch64-linux-gnu-
# 2.3 PATH, 比如: export PATH=$PATH:/home/book/100ask_roc-rk3399-pc/ToolChain-6.3.1/gcc-linaro-6.3.1-2017.05-x86_64_aarch64-linux-gnu/bin
# 注意: 不同的开发板不同的编译器上述3个环境变量不一定相同,
# 请参考各开发板的高级用户使用手册
ARCH=arm
CROSS_COMPILE=arm-linux-gnueabihf-
KERN_DIR = /home/book/embedfire/ebf_6ull_linux
#KERN_DIR = /home/book/100ask_firefly-rk3288/linux-4.4
PWD = $(shell pwd)
all:
make -C $(KERN_DIR) M=$(PWD) modules
$(CROSS_COMPILE)gcc -o led_app led_app.c
clean:
make -C $(KERN_DIR) M=$(PWD) modules clean
rm -rf modules.order
rm -f led_app
# 参考内核源码drivers/char/ipmi/Makefile
# 要想把a.c, b.c编译成ab.ko, 可以这样指定:
# ab-y := a.o b.o
# obj-m += ab.o
# led_drv.c board_fire_imx6ull.c 编译成 xym_led.ko
xym_led-y := led_drv.o board_fire_imx6ull_pro.o
#xym_led-y := led_drv.o board_rk3288.o
obj-m += xym_led.o
```
工程目录结构如下:

10. 修改内核代码的顶层Makefile,切记一定要修改,否则下面编译会出错,即使我们编译驱动模块的makefile里面添加了这两句。

11. 按"**Ctrl+B**"快捷键,进行编译,可看到成功编译,并生成了**xym_led.ko**文件,将该文件复制到开发板,执行**insmod xym_led.ko** 加载该驱动
## Ubuntu使用Eclipse开发嵌入式Linux应用程序
开发应用程序和开发驱动程序很类似,只不过驱动中我们需要使用自己写的makefile,而应用程序我们可以使用Eclipse再带的makefile,不用我们在写了。
步骤如下:
- 第一步:

- 第二步

- 第三步

- 第四步

- 第五步:添加自己的源代码,这些都是根据自己的需求添加

- 文件添加完毕后,编译即可

### Eclipse 应用程序库文件使用
#### 生成静态库
1. 新建工程


2. 配置工程:同样需要导入符号表

3. 在工程里面新建自己的源码文件即可
```c
/*
* math.c
*
* Created on: Nov 14, 2020
* Author: book
*/
int add (int a, int b)
{
return (a+b);
}
```
```c
/*
* math.h
*
* Created on: Nov 14, 2020
* Author: book
*/
#ifndef MATH_H_
#define MATH_H_
extern int add (int a, int b);
#endif /* MATH_H_ */
```
4. 编译即可输出库文件`libmathStaticLib.a`

#### 生成动态库
1. 新建工程


2. 配置工程:同样需要导入符号表

3. 在工程里面新建自己的源码文件即可
```
/*
* math.c
*
* Created on: Nov 14, 2020
* Author: book
*/
int add (int a, int b)
{
return (a+b);
}
```
```
/*
* math.h
*
* Created on: Nov 14, 2020
* Author: book
*/
#ifndef MATH_H_
#define MATH_H_
extern int add (int a, int b);
#endif /* MATH_H_ */
```
4. 编译即可生成动态库`libmathShareLib.so`

#### 嵌入式应用程序使用库文件
##### 静态库使用
需要拷贝刚生成的`libmathStaticLib.a`和头文件`math.h`文件到应用程序目录下,放哪里用户自己组织即可。
1. 添加库文件和库的头文件到工程目录下,自己组织
我的工程目录如下:

2. 添加头文件路径【includes】到工程

3. 添加库文件到工程

4. 编译即可。
##### 动态库使用
使用方法和静态库完全一样。
## Ubuntu使用Eclipse开发嵌入式Linux内核
- 第一步:保证自己的内核可以在终端编译,我的编译步骤如下:
```bash
export ARCH=arm CROSS_COMPILE=arm-linux-
make clean
make imx6_v7_ebf_defconfig
make zImage V=1
等待编译完成
```
- 第二步:新建内核工程


- 第三步:链接内核文件

选择后的工程目录如下图所示:

- 设置make选项

- 添加内核和平台相关的头文件路径

- 设置工程某些文件或者文件夹不编译


过滤后的工程目录效果如下:

- 导出符号表,添加宏定义的符号表,并重新导入到工程
- 第一步:生成symbols_linux.xml备用
```bash
cd ebf_6ull_linux/include/generated/
cat autoconf.h |grep define |awk '{print "" $2 "" $3 ""}' > symbols_linux.xml
```
- 第二步:导出eclipse的符号表到桌面文件名字为:eclipseSymbols.xml

- 第三步:把第一步里面导出来的symbols_linux.xml里面的内容复制到第二步的eclipseSymbols.xml文件的指定位置,并在最前面新增`__KERNEL__1`内容即可

- 第四步重新把eclipseSymbols.xml符号表导入到工程

导入完以后就可以看到Symbols了,然后稍微浏览下这里的宏,有些宏是字符串类型的,会少最后的分号,自己给补上即可,一般都是正确的,所以需要大致浏览下。(该问题是由于在第一步执行脚本的时候我们用awk命令 空格分割autoconf.h里面每一行的内容,当遇见宏定义如下
`#define CONFIG_CMDLINE "noinitrd console=ttymxc0,115200"`)noinitrd后面有个空格所以执行
```bash
cat autoconf.h |grep define |awk '{print "" $2 "" $3 ""}' > symbols_linux.xml
```
脚本的时候,$3等于`"noinitrd`而不是等于`"noinitrd console=ttymxc0,115200"`,如此会在symbols_linux.xml出现下面的定义
```bash
CONFIG_CMDLINE"noinitrd<\macro>
```
实际上我们需要的却是
```bash
CONFIG_CMDLINE"noinitrd console=ttymxc0,115200“<\macro>
```

- 第五步:配置 **C/C++ Gernal**
- 【indexer】
- 勾选 【Enable project specific settings】选
- 去掉【 Index source files not included in the build】

- 【Preprocessor Include Paths】
- 【Enteries】
【GNU C –> CDT User Setting Entries –> Add –> Preprocessor Macros File –> 选择 【include/gernerated/autoconf.h】
所有make menuconfig 时的编译配置信息都在这个文件里,所以需要让eclipse识别这些信息

- 添加生成dtb命令:方便生成dtbs文件

### 使能自动补全快捷键
【Window-Preferences-c/c++-Editor-Content Assist-Advanced】将未勾选的全部勾选
```bash
Alt + / 快捷键:C++自动补全变量名快捷键
Ctrl +Spase 快捷键: 会自动显示代码
```

### Eclipse项目设置过滤
通过make生成的**.o 、mod**等等文件都自动添加到项目架构当中了,但这些文件我们又关心,这就可以增加过滤规则,让项目不自动添加这些文件,设置方式如下图所示【Resource->Resource Filter->Exclude all->Add Filter】

**下面是我自己的配置**:

### Eclipse 设置编译器自动保存文件


### Eclipse 排除文件编译
方法一:屏蔽自己不想加入工程的文件【选中文件或者文件夹,右键 Properties】然后按照下面的方法设置(测试发现,只有源代码.c或者cpp文件才有效,屏蔽库或者.h无效)

方法二:按alt+enter快捷键进入配置先选,添加过滤,这种方式可以整体选择很过个一起添加。如下图,

# Eclipse开发应用程序,cmake篇